package org.aroundme.services;
import java.io.IOException;
import java.util.Map;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.aroundme.model.Location;
import org.aroundme.utility.Pair;

import com.pjaol.search.geo.utils.DistanceQuery;
import com.pjaol.search.geo.utils.DistanceSortSource;
import com.pjaol.search.geo.utils.InvalidGeoException;

/**
 * This class is responsible for searching for nearby places
 */
public class GeoSearcher {
	private Directory directory;
	private IndexSearcher indexSearcher;
	
	//Radius in miles
	private final double RADIUS = 1.0;
	
	public GeoSearcher(Directory directory) throws IOException {
		this.directory = directory;
		init();
	}
	
	/**
	 * initializes the Lucene IndexSearcher
	 * @throws CorruptIndexException
	 * @throws IOException
	 */
	private void init() throws CorruptIndexException, IOException {
		IndexReader reader = IndexReader.open(directory, true);
		indexSearcher = new IndexSearcher(reader);
	}
	
	/**
	 * closes the Lucene IndexSearcher
	 */
	public void close() throws IOException {
		indexSearcher.close();
	}
	
	/**
	 * returns all nearby places for a certain category
	 * @param category
	 * @param location
	 * @return
	 * @throws IOException
	 * @throws InvalidGeoException
	 * @throws ParseException
	 * @throws java.text.ParseException 
	 */
	public Pair<Hits, Map<Integer, Double>> searchByCategory(String category, Location location) throws IOException, InvalidGeoException, ParseException {
		// create a distance query
		DistanceQuery dq = new DistanceQuery(Double.valueOf(location.getLat()), Double.valueOf(location.getLon()), RADIUS, PlaceDocument.LATITUDE, PlaceDocument.LONGITUDE, false);
		 
		System.out.println(dq.latFilter.toString() + " " + dq.lngFilter);
		
		//create a term query to search against all documents that match the specified categories
		Query tq = new TermQuery(new Term(PlaceDocument.CATEGORIES, category));
		
		// Create a distance sort
		// As the radius filter has performed the distance calculations
		// already, pass in the filter to reuse the results.
		// 
		DistanceSortSource dsort = new DistanceSortSource(dq.distanceFilter);
		Sort sort = new Sort(new SortField("foo", dsort));
		
		// Perform the search, using the term query, the serial chain filter, and the
		// distance sort
		Hits hits = indexSearcher.search(tq, dq.getFilter(), sort);
		
		Map<Integer, Double> distances = dq.distanceFilter.getDistances();
		
		return new Pair(hits, distances);
	}
	
	/**
	 * returns all nearby places that match a certain search term
	 * @param term
	 * @param location
	 * @return
	 * @throws IOException
	 * @throws InvalidGeoException
	 * @throws ParseException 
	 * @throws ParseException
	 * @throws java.text.ParseException 
	 */
	public Pair<Hits, Map<Integer, Double>> searchByTerm(String term, Location location) throws IOException, InvalidGeoException, ParseException {
		// create a distance query
		DistanceQuery dq = new DistanceQuery(Double.valueOf(location.getLat()), Double.valueOf(location.getLon()), RADIUS, PlaceDocument.LATITUDE, PlaceDocument.LONGITUDE, false);
		 
		System.out.println(dq.latFilter.toString() + " " + dq.lngFilter);
		
		//create a query to search against all documents that match the specified search term
		QueryParser parser = new QueryParser(PlaceDocument.ALL, new StandardAnalyzer());
		Query tq = parser.parse(term);
		
		// Create a distance sort
		// As the radius filter has performed the distance calculations
		// already, pass in the filter to reuse the results.
		// 
		DistanceSortSource dsort = new DistanceSortSource(dq.distanceFilter);
		Sort sort = new Sort(new SortField("foo", dsort));
		
		// Perform the search, using the term query, the serial chain filter, and the
		// distance sort
		Hits hits = indexSearcher.search(tq, dq.getFilter(), sort);
		
		Map<Integer,Double> distances = dq.distanceFilter.getDistances();
		
		return new Pair(hits, distances);
	}
}
